package com.fary.beans.factory.support;

import com.fary.beans.factory.DisposableBean;
import com.fary.beans.factory.ObjectFactory;
import com.fary.beans.factory.config.SingletonBeanRegistry;
import com.fary.core.SimpleAliasRegistry;
import com.fary.core.SpringException;
import com.fary.util.Assert;
import com.fary.util.StringUtils;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

public class DefaultSingletonBeanRegistry extends SimpleAliasRegistry implements SingletonBeanRegistry {

    /**
     * Cache of singleton objects: bean name to bean instance.
     * 用于存放完全初始化好的bean，从该缓存中取出的bean可以直接使用。
     */
    private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    /**
     * Cache of singleton factories: bean name to ObjectFactory.
     * 存放bean工厂对象，用于解决循环依赖。
     */
    private final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);

    /**
     * Cache of early singleton objects: bean name to bean instance.
     * 存放原始的bean对象(尚未填充属性)，用于解决循环依赖。
     */
    private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);

    /**
     * Set of registered singletons, containing the bean names in registration order.
     */
    private final Set<String> registeredSingletons = new LinkedHashSet<>(256);

    /**
     * Names of beans that are currently in creation.
     * 正在创建的单例bean集合
     */
    private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * Names of beans currently excluded from in creation checks.
     */
    private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));

    /**
     * List of suppressed Exceptions, available for associating related causes.
     * 被抑制的异常列表，可用于关联相关原因。
     */
    private Set<Exception> suppressedExceptions;

    /**
     * Flag that indicates whether we're currently within destroySingletons.
     * 标志表明我们目前是否在销毁所有singleton。
     */
    private boolean singletonsCurrentlyInDestruction = false;

    /**
     * Disposable bean instances: bean name to disposable instance.
     */
    private final Map<String, Object> disposableBeans = new LinkedHashMap<>();

    /**
     * Map between containing bean names: bean name to Set of bean names that the bean contains.
     */
    private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);

    /**
     * Map between dependent bean names: bean name to Set of dependent bean names.
     */
    private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);

    /**
     * Map between depending bean names: bean name to Set of bean names for the bean's dependencies.
     */
    private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);

    @Override
    public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
        Assert.notNull(beanName, "Bean name must not be null");
        Assert.notNull(singletonObject, "Singleton object must not be null");
        synchronized (this.singletonObjects) {
            Object oldObject = this.singletonObjects.get(beanName);
            if (oldObject != null) {
                throw new IllegalStateException("Could not register object [" + singletonObject +
                        "] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
            }
            addSingleton(beanName, singletonObject);
        }
    }

    /**
     * 从缓存中获取单例对象
     */
    @Override
    public Object getSingleton(String beanName) {
        return getSingleton(beanName, true);
    }

    /**
     * 从缓存中获取单例对象
     */
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
        // 检查单例缓存中是否存在实例
        Object singletonObject = this.singletonObjects.get(beanName);
        // 只有正在创建的bean才进一步的处理
        if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
            synchronized (this.singletonObjects) {
                // 从早期缓存获取bean实例
                singletonObject = this.earlySingletonObjects.get(beanName);
                if (singletonObject == null && allowEarlyReference) {
                    // 早期缓存中不存在bean实例，就从singletonFactories获取bean对应的ObjectFactory
                    // 每个bean在实例化的时候，都会包装一层ObjectFactory放到singletonFactories中，然后使用ObjectFactory的getObject()方法创建bean实例
                    ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                    if (singletonFactory != null) {
                        // 调用ObjectFactory的getObject()方法创建bean实例
                        singletonObject = singletonFactory.getObject();
                        // 放到早期缓存中
                        this.earlySingletonObjects.put(beanName, singletonObject);
                        // singletonFactories移除该bean对应的ObjectFactory，因为已经使用过了
                        this.singletonFactories.remove(beanName);
                    }
                }
            }
        }
        return singletonObject;
    }

    /**
     * 创建单例bean，从单例bean工厂中获取
     * 单例对象实现，如果已经创建完毕直接从单例缓存中取，如果是初次访问才会调用ObjectFactory触发createBean创建实例。
     *   在创建对象之前 会将当前要创建的对象先添加到singletonsCurrentlyInCreation这个Set中，同时能起到
     *   判断循环依赖注入的问题。
     *   比如A实例的创建需要通过构造器注入B实例，这时会触发B实例的getBean(b) 来创建B的实例，
     *   在创建B的过程中发现B 的构造器需要注入A的实例，而这时 又会进行getBean(a) 这时A实例已经存在于
     *   singletonsCurrentlyInCreation这个Set中，在创建前会先添加进去，如果添加失败，则表示存在循环注入
     * @param beanName
     * @param singletonFactory
     * @return
     */
    public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(beanName, "Bean name must not be null");
        // 1.加锁，避免重复创建单例对象
        synchronized (this.singletonObjects) {
            // 2.首先检查beanName对应的bean实例是否在缓存中存在，如果已经存在，则直接返回
            Object singletonObject = this.singletonObjects.get(beanName);
            if (singletonObject == null) {
                // 3.beanName对应的bean实例不存在于缓存中，则进行Bean的创建
                if (this.singletonsCurrentlyInDestruction) {
                    // 4.当bean工厂的单例处于destruction状态时，不允许进行单例bean创建，抛出异常
                    throw new SpringException(beanName + " Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!)");
                }
                // 5.创建单例前的操作
                beforeSingletonCreation(beanName);
                boolean newSingleton = false;
                try {
                    // 6.执行singletonFactory的getObject方法获取bean实例
                    singletonObject = singletonFactory.getObject();
                    // 标记为新的单例对象
                    newSingleton = true;
                } catch (IllegalStateException ex) {
                    singletonObject = this.singletonObjects.get(beanName);
                    if (singletonObject == null) {
                        throw ex;
                    }
                } finally {
                    // 7.创建单例后的操作
                    afterSingletonCreation(beanName);
                }
                if (newSingleton) {
                    // 8.如果是新的单例对象，将beanName和对应的bean实例添加到缓存中（singletonObjects、registeredSingletons）
                    addSingleton(beanName, singletonObject);
                }
            }
            // 9.返回创建出来的单例对象
            return singletonObject;
        }
    }

    /**
     * 单例实例对象创建：
     * 1.判断是否在排除之外
     * 2.向正在创建单例对象的集合中添加实例对象
     */
    protected void beforeSingletonCreation(String beanName) {
        // 先校验beanName是否为要在创建检查排除掉的（inCreationCheckExclusions缓存），如果不是，
        // 则将beanName加入到正在创建bean的缓存中（Set），如果beanName已经存在于该缓存，会返回false抛出异常（这种情况出现在构造器的循环依赖）
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
            throw new SpringException(beanName + " Requested bean is currently in creation: Is there an unresolvable circular reference?");
        }
    }

    /**
     * 单例实例对象创建完之后：
     * 1.判断是否在排除之外
     * 2.移除正在创建单例对象的集合中的实例对象
     */
    protected void afterSingletonCreation(String beanName) {
        // 先校验beanName是否为要在创建检查排除掉的（inCreationCheckExclusions缓存），如果不是，
        // 则将beanName从正在创建bean的缓存中（Set）移除，如果beanName不存在于该缓存，会返回false抛出异常
        if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
            throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
        }
    }

    /**
     * 判断指定名称的单例是否在创建中
     */
    public boolean isSingletonCurrentlyInCreation(String beanName) {
        return this.singletonsCurrentlyInCreation.contains(beanName);
    }

    /**
     * 判断指定名称的单例是否已经创建好
     */
    @Override
    public boolean containsSingleton(String beanName) {
        return this.singletonObjects.containsKey(beanName);
    }

    public void destroySingleton(String beanName) {
        // Remove a registered singleton of the given name, if any.
        removeSingleton(beanName);

        // Destroy the corresponding DisposableBean instance.
        DisposableBean disposableBean;
        synchronized (this.disposableBeans) {
            disposableBean = (DisposableBean) this.disposableBeans.remove(beanName);
        }
        destroyBean(beanName, disposableBean);
    }

    protected void addSingleton(String beanName, Object singletonObject) {
        synchronized (this.singletonObjects) {
            // 1.添加到单例对象缓存
            this.singletonObjects.put(beanName, singletonObject);
            // 2.将单例工厂缓存移除（已经不需要）
            this.singletonFactories.remove(beanName);
            // 3.将早期单例对象缓存移除（已经不需要）
            this.earlySingletonObjects.remove(beanName);
            // 4.添加到已经注册的单例对象缓存
            this.registeredSingletons.add(beanName);
        }
    }

    protected void removeSingleton(String beanName) {
        synchronized (this.singletonObjects) {
            this.singletonObjects.remove(beanName);
            this.singletonFactories.remove(beanName);
            this.earlySingletonObjects.remove(beanName);
            this.registeredSingletons.remove(beanName);
        }
    }

    /**
     * 检测beaName和dependsOn是否存在已存在依赖
     *
     * @param beanName
     * @param dependentBeanName 被依赖的bean名称
     */
    protected boolean isDependent(String beanName, String dependentBeanName) {
        synchronized (this.dependentBeanMap) {
            return isDependent(beanName, dependentBeanName, null);
        }
    }

    /**
     * 检测beaName和dependsOn是否存在已存在依赖
     * <p>
     * A依赖于B，B依赖于C，C依赖于A
     * 1.A依赖于B，dependentBeanMap中key为A的集合为空，直接返回false，并注册B依赖于A
     * 2.B依赖于C，dependentBeanMap中key为B的集合不为空，这个集合不包含C，递归判断集合中的A依赖于C？（如果集合中有A->C，说明之前有C依赖于A，循环依赖了，报错），并注册C依赖于B
     * 3.C依赖于A，dependentBeanMap中key为C的集合不为空，这个集合不包含A，递归判断集合中的B依赖于A？，
     * B依赖于A，dependentBeanMap中key为B的集合不为空，这个集合包含A，直接返回true
     */
    private boolean isDependent(String beanName, String dependentBeanName, Set<String> alreadySeen) {
        // 已经检查过的直接跳过
        if (alreadySeen != null && alreadySeen.contains(beanName)) {
            return false;
        }
        // 1.将别名解析为真正的名称
        String canonicalName = canonicalName(beanName);
        // 2.拿到依赖canonicalName的beanName集合
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        // 3.如果dependentBeans为空，则两者必然还未确定依赖关系，返回false
        if (dependentBeans == null) {
            return false;
        }
        // dependentBeans不为空，则可能依赖多个 A-》B  A-》C
        // beaName-》dependentBeanName   dependentBeanName-》beaName  beaName-》dependentBeanName
        // 4.如果dependentBeans包含dependentBeanName，则表示两者已确定依赖关系，返回true
        if (dependentBeans.contains(dependentBeanName)) {
            return true;
        }
        // 5.循环检查，即检查依赖canonicalName的所有beanName是否存在被dependentBeanName依赖的（即隔层依赖）
        for (String transitiveDependency : dependentBeans) {
            if (alreadySeen == null) {
                alreadySeen = new HashSet<>();
            }
            // 6.已经检查过的添加到alreadySeen，避免重复检查
            alreadySeen.add(beanName);
            if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 注册依赖bean
     *
     * 例如 B 依赖了 A，则 dependentBeanMap 缓存中应该存放一对映射：其中 key 为 A，value 为含有 B 的 Set；
     * 而 dependenciesForBeanMap 缓存中也应该存放一对映射：其中 key 为：B，value 为含有 A 的 Set。
     */
    public void registerDependentBean(String beanName, String dependentBeanName) {
        // 1.解析别名
        String canonicalName = canonicalName(beanName);

        // 4.如果依赖关系还没有注册，则将两者的关系注册到dependentBeanMap和dependenciesForBeanMap缓存
        synchronized (this.dependentBeanMap) {
            Set<String> dependentBeans = this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
            if (!dependentBeans.add(dependentBeanName)) {
                return;
            }
        }

        synchronized (this.dependenciesForBeanMap) {
            Set<String> dependenciesForBean = this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8));
            dependenciesForBean.add(canonicalName);
        }
    }

    protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) {
        Assert.notNull(singletonFactory, "Singleton factory must not be null");
        synchronized (this.singletonObjects) {
            // 1.如果beanName不存在于singletonObjects缓存中
            if (!this.singletonObjects.containsKey(beanName)) {
                // 2.将beanName和singletonFactory注册到singletonFactories缓存（beanName -> 该beanName的单例工厂）
                this.singletonFactories.put(beanName, singletonFactory);
                // 3.移除earlySingletonObjects缓存中的beanName（beanName -> beanName的早期单例对象）
                this.earlySingletonObjects.remove(beanName);
                // 4.将beanName注册到registeredSingletons缓存（已经注册的单例集合）
                this.registeredSingletons.add(beanName);
            }
        }
    }

    public boolean isCurrentlyInCreation(String beanName) {
        Assert.notNull(beanName, "Bean name must not be null");
        return (!this.inCreationCheckExclusions.contains(beanName) && isActuallyInCreation(beanName));
    }

    protected boolean isActuallyInCreation(String beanName) {
        return isSingletonCurrentlyInCreation(beanName);
    }

    public void registerContainedBean(String containedBeanName, String containingBeanName) {
        synchronized (this.containedBeanMap) {
            Set<String> containedBeans = this.containedBeanMap.computeIfAbsent(containingBeanName, k -> new LinkedHashSet<>(8));
            if (!containedBeans.add(containedBeanName)) {
                return;
            }
        }
        registerDependentBean(containedBeanName, containingBeanName);
    }

    protected void destroyBean(String beanName, DisposableBean bean) {
        // Trigger destruction of dependent beans first...
        Set<String> dependencies;
        synchronized (this.dependentBeanMap) {
            // Within full synchronization in order to guarantee a disconnected Set
            dependencies = this.dependentBeanMap.remove(beanName);
        }
        if (dependencies != null) {
            for (String dependentBeanName : dependencies) {
                destroySingleton(dependentBeanName);
            }
        }

        // Actually destroy the bean now...
        if (bean != null) {
            try {
                bean.destroy();
            } catch (Throwable ex) {

            }
        }

        // Trigger destruction of contained beans...
        Set<String> containedBeans;
        synchronized (this.containedBeanMap) {
            // Within full synchronization in order to guarantee a disconnected Set
            containedBeans = this.containedBeanMap.remove(beanName);
        }
        if (containedBeans != null) {
            for (String containedBeanName : containedBeans) {
                destroySingleton(containedBeanName);
            }
        }

        // Remove destroyed bean from other beans' dependencies.
        synchronized (this.dependentBeanMap) {
            for (Iterator<Map.Entry<String, Set<String>>> it = this.dependentBeanMap.entrySet().iterator(); it.hasNext(); ) {
                Map.Entry<String, Set<String>> entry = it.next();
                Set<String> dependenciesToClean = entry.getValue();
                dependenciesToClean.remove(beanName);
                if (dependenciesToClean.isEmpty()) {
                    it.remove();
                }
            }
        }

        // Remove destroyed bean's prepared dependency information.
        this.dependenciesForBeanMap.remove(beanName);
    }

    protected boolean hasDependentBean(String beanName) {
        return this.dependentBeanMap.containsKey(beanName);
    }

    public String[] getDependentBeans(String beanName) {
        Set<String> dependentBeans = this.dependentBeanMap.get(beanName);
        if (dependentBeans == null) {
            return new String[0];
        }
        synchronized (this.dependentBeanMap) {
            return StringUtils.toStringArray(dependentBeans);
        }
    }

    public void registerDisposableBean(String beanName, DisposableBean bean) {
        synchronized (this.disposableBeans) {
            this.disposableBeans.put(beanName, bean);
        }
    }

    public void destroySingletons() {
        synchronized (this.singletonObjects) {
            this.singletonsCurrentlyInDestruction = true;
        }

        String[] disposableBeanNames;
        synchronized (this.disposableBeans) {
            disposableBeanNames = StringUtils.toStringArray(this.disposableBeans.keySet());
        }
        for (int i = disposableBeanNames.length - 1; i >= 0; i--) {
            destroySingleton(disposableBeanNames[i]);
        }

        this.containedBeanMap.clear();
        this.dependentBeanMap.clear();
        this.dependenciesForBeanMap.clear();

        clearSingletonCache();
    }

    protected void clearSingletonCache() {
        synchronized (this.singletonObjects) {
            this.singletonObjects.clear();
            this.singletonFactories.clear();
            this.earlySingletonObjects.clear();
            this.registeredSingletons.clear();
            this.singletonsCurrentlyInDestruction = false;
        }
    }

    @Override
    public String[] getSingletonNames() {
        return new String[0];
    }

    @Override
    public int getSingletonCount() {
        return 0;
    }

    @Override
    public Object getSingletonMutex() {
        return this.singletonObjects;
    }
}